#pragma once

#include<iostream>
#include<string>
#include<vector>
#include<fstream>
#include<unordered_map>
#include<mutex>
#include"util.hpp"

namespace us_index
{
    struct DocInfo
    {
        std::string title;  //文档标题
        std::string content;//文档去标签后的内容
        std::string url;    //官网的url
        uint64_t doc_id;    //文档的索引id
    };

    struct InvertedElem
    {
        uint64_t doc_id;
        std::string word;
        int weight;
    };
    typedef std::vector<InvertedElem> InvertedList;
    class Index
    {
        private:
            // 一个id对应一个文档
            std::vector<DocInfo> forward_index; //正排索引
            // 一个关键字对应一串id， 关键字和倒排拉链的对应关系
            std::unordered_map<std::string,InvertedList> inverted_index; //倒排索引
            Index(){}
            Index(const Index&) = delete;
            Index& operator=(const Index&) = delete;
            static Index* instance;
            static std::mutex mtx;
        public:
            ~Index(){}
        public:
            static Index* GetInstance()
            {
                if(instance == nullptr)
                {
                    mtx.lock();
                    if(instance  == nullptr)
                    {
                        instance = new Index();
                    }
                    mtx.unlock();
                }
                return instance;
            }
            DocInfo* GetForwardIndex(const uint64_t& doc_id) //获取整排索引
            {
                if(doc_id>=forward_index.size())
                {
                    return nullptr;
                }

                return &forward_index[doc_id];
            }

            InvertedList* GetInvertedIndex(const std::string& word) 
            {
                auto iter = inverted_index.find(word);
                if(iter == inverted_index.end())
                {
                    return nullptr;
                }
                
                return &(iter->second);
            }

            // 根据之前构建的2进制文件建立索引
            bool BuildIndex(const std::string& input)
            {
                std::ifstream in(input,std::ios::in | std::ios::binary);
                std::string line;
                int num=0;
                while(getline(in,line))
                {
                    if(num%50==0)
                    {
                        std::cout<<"已建立索引"<<num<<"个"<<std::endl;
                    }
                    DocInfo* doc = BuildForwardIndex(line);
                    if(doc == nullptr)
                    {
                        std::cerr<<"debug "<<line<<" 正排索引构建失败"<<std::endl;
                        continue;
                    }

                    if(!BuildInveredIndex(*doc))
                    {
                        std::cerr<<"debug "<<line<<"倒排索引构建失败"<<std::endl;
                        continue;
                    }
                    num++;
                }
                std::cout<<"索引建立完毕"<<std::endl;
                return true;
            }

        private:
            DocInfo* BuildForwardIndex(std::string& line)
            {
                //之前是用\3分割文档的三个内容，现在用\3切割就可以还原
                const std::string sep="\3";
                std::vector<std::string> results;
                us_util::StringUitl::Split(line,&results,sep);

                if(results.size() != 3)
                {
                    return nullptr;
                }

                DocInfo doc;
                doc.title=results[0];
                doc.content=results[1];
                doc.url=results[2];
                doc.doc_id=forward_index.size();

                forward_index.push_back(std::move(doc));

                return &forward_index.back();
            }

            

            bool BuildInveredIndex(const DocInfo& doc)
            {
                const int content_pow=1,title_pow=10;//正文权值，标题权值

                struct word_time
                {
                    int title_time;
                    int content_time;

                    word_time(int t=0,int c=0):title_time(t),content_time(c){}
                };
                std::unordered_map<std::string,word_time> word_map;

                // 对标题进行分词
                std::vector<std::string> title_word;
                us_util::JiebaUtil::CutString(doc.title,&title_word);

                for(std::string s:title_word) //词频统计
                {
                    us_util::StringUitl::to_lower(s);
                    word_map[s].title_time++;
                }

                std::vector<std::string> content_word;
                us_util::JiebaUtil::CutString(doc.title,&content_word);

                for(std::string s:title_word) //词频统计
                {
                    us_util::StringUitl::to_lower(s);
                    word_map[s].content_time++;
                }
            
                // 建立倒排索引
                for(auto& word_pair: word_map)
                {
                    InvertedElem item;
                    item.doc_id=doc.doc_id;
                    item.word=word_pair.first;
                    item.weight=word_pair.second.content_time*content_pow\
                    +word_pair.second.title_time*title_pow;

                    auto& index_list = inverted_index[word_pair.first];
                    index_list.push_back(std::move(item));
                }
                return true;
            }
    };
    Index* Index::instance = nullptr;
    std::mutex Index::mtx;


};